package com.liyuci.core.helper;

import java.math.BigDecimal;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.regex.Pattern;

/**
 * 数字操作工具类（防止数字失真）.
 * @author liyuci
 * @date Sep 13, 2017 6:47:29 PM
 */
public class MathHelper
{
	/** 默认除法运算精度. */
	private static final int DEF_DIV_SCALE = 10;

	/**
	 * 提供精确的加法运算.
	 * @author liyuci
	 * @date 2017年9月13日 下午3:38:15
	 * @param v1	被加数
	 * @param v2	加数
	 * @return		两个参数的和
	 */
	public static Double add(Double v1, Double v2)
	{
		BigDecimal b1 = new BigDecimal(Double.toString(v1));
		BigDecimal b2 = new BigDecimal(Double.toString(v2));
		return b1.add(b2).doubleValue();
	}

	/**
	 * 提供精确的减法运算.
	 * @author liyuci 
	 * @date 2017年9月13日 下午3:38:15
	 * @param v1	被减数
	 * @param v2	减数
	 * @return		两个参数的差
	 */
	public static Double sub(Double v1, Double v2)
	{
		BigDecimal b1 = new BigDecimal(Double.toString(v1));
		BigDecimal b2 = new BigDecimal(Double.toString(v2));
		return b1.subtract(b2).doubleValue();
	}

	/**
	 * 提供精确的乘法运算.
	 * @author liyuci 
	 * @date 2017年9月13日 下午3:38:15
	 * @param v1	被乘数
	 * @param v2	乘数
	 * @return 		两个参数的积
	 */
	public static Double mul(Double v1, Double v2)
	{
		BigDecimal b1 = new BigDecimal(Double.toString(v1));
		BigDecimal b2 = new BigDecimal(Double.toString(v2));
		return b1.multiply(b2).doubleValue();
	}

	/**
	 * 提供精确的乘法运算.
	 * @author liyuci 
	 * @date 2017年9月13日 下午3:38:15
	 * @param v1		被乘数
	 * @param v2		乘数
	 * @param scale		保留几位小数
	 * @return			两个参数的积
	 */
	public static Double mul(Double v1, Double v2, int scale)
	{
		Double v = mul(v1, v2);
		return round(v, scale);
	}

	/**
	 * 提供（相对）精确的除法运算，当发生除不尽的情况时，精确到 小数点以后10位，以后的数字四舍五入.
	 * @author liyuci 
	 * @date 2017年9月13日 下午3:38:15
	 * @param v1	被除数
	 * @param v2	除数
	 * @return 		两个参数的商
	 */
	public static Double div(Double v1, Double v2)
	{
		return div(v1, v2, DEF_DIV_SCALE);
	}

	/**
	 * 提供（相对）精确的除法运算。当发生除不尽的情况时，由scale参数指 定精度，以后的数字四舍五入.
	 * @author liyuci
	 * @date 2017年9月13日 下午3:38:15
	 * @param v1	被除数
	 * @param v2	除数
	 * @param scale	表示表示需要精确到小数点以后几位。
	 * @return 		两个参数的商
	 */
	public static Double div(Double v1, Double v2, int scale)
	{
		if(scale < 0)
		{
			throw new IllegalArgumentException("请指定小数点精度，不能小于0");
		}
		BigDecimal b1 = new BigDecimal(Double.toString(v1));
		BigDecimal b2 = new BigDecimal(Double.toString(v2));
		return b1.divide(b2, scale, BigDecimal.ROUND_HALF_UP).doubleValue();
	}

	/**
	 * 提供精确的小数位四舍五入处理.
	 * @author liyuci 
	 * @date 2017年9月13日 下午3:38:15
	 * @param v		需要四舍五入的数字
	 * @param scale	小数点后保留几位
	 * @return 		四舍五入后的结果
	 */
	public static Double round(Double v, int scale)
	{
		if(scale < 0)
		{
			throw new IllegalArgumentException("请指定小数点精度，不能小于0");
		}
		BigDecimal b = new BigDecimal(Double.toString(v));
		BigDecimal one = new BigDecimal("1");
		return b.divide(one, scale, BigDecimal.ROUND_HALF_UP).doubleValue();
	}

	/**
	 * 提供精确的小数位四舍五入处理.
	 * @author liyuci 
	 * @date 2017年9月13日 下午3:45:26
	 * @param v		需要四舍五入的数字
	 * @return		四舍五入后的结果
	 */
	private static BigDecimal round(Double v)
	{
		BigDecimal b = new BigDecimal(Double.toString(v));
		BigDecimal one = new BigDecimal("1");
		return b.divide(one, DEF_DIV_SCALE, BigDecimal.ROUND_HALF_UP);
	}

	/**
	 * 提供精确的类型转换(Float).
	 * @author liyuci 
	 * @date 2017年9月13日 下午3:38:15
	 * @param v		需要被转换的数字
	 * @return 		返回转换结果
	 */
	public static float convertsToFloat(Double v)
	{
		BigDecimal b = new BigDecimal(v);
		return b.floatValue();
	}

	/**
	 * 提供精确的类型转换(Int)不进行四舍五入.
	 * @author liyuci 
	 * @date 2017年9月13日 下午3:38:15
	 * @param v	需要被转换的数字
	 * @return 	返回转换结果
	 */
	public static int convertsToInt(Double v)
	{
		BigDecimal b = new BigDecimal(v);
		return b.intValue();
	}

	/**
	 * 提供精确的类型转换(Long).
	 * @author liyuci 
	 * @date 2017年9月13日 下午3:38:15
	 * @param v	需要被转换的数字
	 * @return	返回转换结果
	 */
	public static long convertsToLong(Double v)
	{
		BigDecimal b = new BigDecimal(v);
		return b.longValue();
	}

	/**
	 * 精确对比两个数字.
	 * @author liyuci 
	 * @date 2017年9月13日 下午3:38:15
	 * @param v1	需要对比的第一个数
	 * @param v2	需要对比的第二个数
	 * @return 		如果两个数一样则返回0，如果第一个数比第二个数大则返回1，反之返回-1
	 */
	private static int compareTo(Double v1, Double v2)
	{
		BigDecimal b1 = round(v1);
		BigDecimal b2 = round(v2);
		return b1.compareTo(b2);
	}

	/**
	 * 返回相反数.
	 * @author liyuci 
	 * @date 2017年9月13日 下午3:38:15
	 * @param v	需要被取反的数
	 * @return	正数变负数，负数变正数
	 */
	public static Double returnNegate(Double v)
	{
		BigDecimal b = new BigDecimal(v);
		return b.negate().doubleValue();
	}

	/**
	 * 两个数字是否相等.
	 * @author liyuci 
	 * @date 2017年9月13日 下午3:38:15
	 * @param v1	需要对比的第一个数
	 * @param v2	需要对比的第二个数
	 * @return		true|false
	 */
	public static boolean isEQ(Double v1, Double v2)
	{
		int equalNum = compareTo(v1, v2);
		if(equalNum == 0)
		{
			return true;
		}
		return false;
	}

	/**
	 * v1是否大于v2.
	 * @author liyuci 
	 * @date 2017年9月13日 下午3:38:15
	 * @param v1	需要对比的第一个数
	 * @param v2	需要对比的第二个数
	 * @return		true|false
	 */
	public static boolean isGT(Double v1, Double v2)
	{
		int equalNum = compareTo(v1, v2);
		if(equalNum > 0)
		{
			return true;
		}
		return false;
	}

	/**
	 * v1是否小于v2.
	 * @author liyuci 
	 * @date 2017年9月13日 下午3:38:15
	 * @param v1	需要对比的第一个数
	 * @param v2	需要对比的第二个数
	 * @return		true|false
	 */
	public static boolean isLT(Double v1, Double v2)
	{
		int equalNum = compareTo(v1, v2);
		if(equalNum < 0)
		{
			return true;
		}
		return false;
	}

	/**
	 * 返回两个数中大的一个值.
	 * @author liyuci 
	 * @date 2017年9月13日 下午3:38:15
	 * @param v1	第一个数
	 * @param v2	第二个数
	 * @return		返回两个数中大的一个值
	 */
	public static Double getMax(Double v1, Double v2)
	{
		BigDecimal b1 = new BigDecimal(v1);
		BigDecimal b2 = new BigDecimal(v2);
		return b1.max(b2).doubleValue();
	}

	/**
	 * 返回两个数中小的一个值.
	 * @author liyuci 
	 * @date 2017年9月13日 下午3:38:15
	 * @param v1	第一个数
	 * @param v2	第二个数
	 * @return		返回两个数中小的一个值
	 */
	public static Double getMin(Double v1, Double v2)
	{
		BigDecimal b1 = new BigDecimal(v1);
		BigDecimal b2 = new BigDecimal(v2);
		return b1.min(b2).doubleValue();
	}

	/**
	 * 取得集合中最大的数字.
	 * @author liyuci 
	 * @date 2017年9月13日 下午3:51:06
	 * @param list		Double集合
	 * @return			集合中最大的数字
	 */
	public static Double getMaxForDouble(List<Double> list)
	{
		Double v = null;
		if(null != list && list.size() > 0)
		{
			v = Collections.max(list);
		}
		return v;
	}

	/**
	 * 取得集合中最大的数字.
	 * @author liyuci 
	 * @date 2017年9月13日 下午3:51:06
	 * @param set		Double集合
	 * @return			集合中最大的数字
	 */
	public static Double getMaxForDouble(Set<Double> set)
	{
		Double v = null;
		if(null != set && set.size() > 0)
		{
			v = Collections.max(set);
		}
		return v;
	}

	/**
	 * 取得集合中最小的数字.
	 * @author liyuci 
	 * @date 2017年9月13日 下午3:51:06
	 * @param list		Double集合
	 * @return			集合中最小的数字
	 */
	public static Double getMinForDouble(List<Double> list)
	{
		Double v = null;
		if(null != list && list.size() > 0)
		{
			v = Collections.min(list);
		}
		return v;
	}

	/**
	 * 取得集合中最小的数字.
	 * @author liyuci 
	 * @date 2017年9月13日 下午3:51:06
	 * @param set		Double集合
	 * @return			集合中最小的数字
	 */
	public static Double getMinForDouble(Set<Double> set)
	{
		Double v = null;
		if(null != set && set.size() > 0)
		{
			v = Collections.min(set);
		}
		return v;
	}

	/**
	 * 取得集合中最大的数字.
	 * @author liyuci 
	 * @date 2017年9月13日 下午3:51:06
	 * @param list		Integer集合
	 * @return			集合中最大的数字
	 */
	public static Integer getMaxForInt(List<Integer> list)
	{
		Integer v = null;
		if(null != list && list.size() > 0)
		{
			v = Collections.max(list);
		}
		return v;
	}

	/**
	 * 取得集合中最大的数字.
	 * @author liyuci 
	 * @date 2017年9月13日 下午3:51:06
	 * @param set		Integer集合
	 * @return			集合中最大的数字
	 */
	public static Integer getMaxForInt(Set<Integer> set)
	{
		Integer v = null;
		if(null != set && set.size() > 0)
		{
			v = Collections.max(set);
		}
		return v;
	}

	/**
	 * 取得集合中最小的数字.
	 * @author liyuci 
	 * @date 2017年9月13日 下午3:51:06
	 * @param list		Integer集合
	 * @return			集合中最小的数字
	 */
	public static Integer getMinForInt(List<Integer> list)
	{
		Integer v = null;
		if(null != list && list.size() > 0)
		{
			v = Collections.min(list);
		}
		return v;
	}

	/**
	 * 取得集合中最小的数字.
	 * @author liyuci 
	 * @date 2017年9月13日 下午3:51:06
	 * @param set		Integer集合
	 * @return			集合中最小的数字
	 */
	public static Integer getMinForInt(Set<Integer> set)
	{
		Integer v = null;
		if(null != set && set.size() > 0)
		{
			v = Collections.min(set);
		}
		return v;
	}

	/**
	 * 是否是整数
	 * @author liyuci
	 * @date 2018-01-18 21:08:59
	 * @param value
	 * @return
	 */
	public static boolean isInt(String value)
	{
		Pattern pattern = Pattern.compile("[0-9]*");
		return pattern.matcher(value).matches();
	}
	
	/**
	 * 截取字符串是数字的前缀信息
	 * @author liyuci
	 * @date 2018-01-18 21:10:02
	 * @param str
	 * @return
	 */
	public static Integer getInt(String str)
	{
		int result = 0;
		if(BlankHelper.isNotBlank(str)
				&& BlankHelper.isNotBlank(str.trim()))
		{
			str = str.trim();
			String resultStr = "";
			int count = str.length();
			for(int i = 1; i <= count; i++)
			{
				String numValue = str.substring(0, i);
				if(isInt(numValue))
				{
					resultStr = numValue;
				}
				else
				{
					break;
				}
			}
			if(BlankHelper.isNotBlank(resultStr))
			{
				result = Integer.parseInt(resultStr);
			}
		}
		return result;
	}
	
}
